package maze;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/**
 * "Intersection", "Door", etc.- relevant to <strong>Controls</strong> group, undecided if <strong>Mechanisms</strong> should have to deal with this (currently it does, but I'm trying to abstract it away).
 * <p>
 * Internal representation of every intersection/door.
 * This abstracts locations to nodes on a graph, so that the A* search algorithm 
 * (or at the current moment, A* with hVal always zero, i.e., Dijkstra's) can be
 * applied to it.
 * </p>
 * 
 * Development stage: should be usable as-is.
 * @author Calvin
 */
public class Node implements Comparable<Node> {
	/*
	 * The h() value given to a node for which no h() value has been computed.
	 * 
	 * If actual A* is used, this will have to be 0 (since it is added to the g() value).
	 */
	public static final double UNKNOWN_H = 0d;
	
	// TODO: encapsulate these
	public HashMap<Vector, Node> neighbors = new HashMap<Vector, Node>();
	public Set<Direction> openDirections = new HashSet<Direction>();
	private Double pathCost;
	private Double heuristic;
	
	public Node(double pathCost, double heuristic, ArrayList<Direction> open){
		this.pathCost = Double.valueOf(pathCost);
		this.heuristic = Double.valueOf(heuristic);
		this.openDirections.addAll(open);
	}
	public Node(){
		this.pathCost = null;
		this.heuristic = null;
	}
	
	/**
	 * Add a neighbor node.
	 * @param neighbor The neighbor node.
	 * @param edge The Vector connecting the current node to the neighbor.
	 */
	public void addNeighbor(Node neighbor, Vector edge){
		neighbors.put(edge, neighbor);
	}
	
	public Node getNodeAt(Direction d){
		for( Vector v : neighbors.keySet()){
			if(v.direction.equals(d)){
				return neighbors.get(v);
			}
		}
		return null;
	}

	public Vector getVectorTo(Node n) {
		if (neighbors.containsValue(n)) {
			for (Vector v : neighbors.keySet()) {
				if (neighbors.get(v).equals(n)) {
					return v;
				}
			}
			// An exception would filter up. An assertion fails right here, immediately.
			// NB: Assertions are turned off by default in JVM and don't work in ME.
			assert true==false;
			return null;
		} else {
			return null;
		}
	}
	
	/**
	 * A* specific.
	 * @param cost The "path cost" for this node.
	 */
	public void setPathCost(double cost){
		this.pathCost = Double.valueOf(cost);
	}
	/**
	 * A* specific.
	 * @param val The value of the heuristic function for this node.
	 */
	public void setHeuristic(double val){
		this.heuristic = Double.valueOf(val);
	}
	
	/**
	 * A* specific.
	 * @return The value at this node of the "path cost" in A* parlance.
	 */
	public double pathCost(){
		return pathCost.doubleValue();
	}
	/**
	 * A* specific.
	 * @return The value of the heuristic function for this node.
	 */
	public double heuristic(){
		// Note that this returns a double value rather than a reference to the Double.
		return heuristic.doubleValue();
	}
	
	/**
	 * A* specific.
	 * Nodes are sorted via some combination of the gVal and hVal.
	 * In my experience this has required lots of tuning.
	 * @return
	 */
	public double overallScore(){
		return pathCost + heuristic;
	}

	@Override
	public int compareTo(Node o) {
		// Note the inherent tiebreaker.
		return ((overallScore() < o.overallScore()) ? -1 : 1);
	}
}
